package org.dragonnova.business.web;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map.Entry;

import javax.servlet.http.HttpSession;

import org.dragonnova.business.common.Constants;
import org.dragonnova.business.dto.WSResponseDto;
import org.dragonnova.business.model.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.TreeMultimap;

public class WebSocketSessionManager {

	private final static Logger LOGGER = LoggerFactory
			.getLogger(WebSocketSessionManager.class);
	private Multimap<Integer, ConcurrentWebSocketSessionDecorator> webSockets;

	public WebSocketSessionManager() {
		webSockets = Multimaps.synchronizedSetMultimap(TreeMultimap.create(
				new Comparator<Integer>() {

					@Override
					public int compare(Integer o1, Integer o2) {
						return Integer.compare(o1, o2);
					}

				}, new Comparator<ConcurrentWebSocketSessionDecorator>() {

					@Override
					public int compare(ConcurrentWebSocketSessionDecorator o1,
							ConcurrentWebSocketSessionDecorator o2) {
						return o1.getId().compareTo(o2.getId());
					}
				}));
	}

	public Collection<Entry<Integer, ConcurrentWebSocketSessionDecorator>> getWebSockets() {
		Collection<Entry<Integer, ConcurrentWebSocketSessionDecorator>> result = Collections
				.unmodifiableCollection(webSockets.entries());
		return result;
	}

	/**
	 * 判断客户端是否存在
	 * 
	 * @param id
	 * @return
	 */
	public boolean isSocketExit(Integer id) {
		if (webSockets.containsKey(id)) {
			return true;
		}
		return false;
	}

	/**
	 * 判断客户端是否存在
	 * 
	 * @param session
	 * @return
	 */
	public boolean isSocketExit(ConcurrentWebSocketSessionDecorator session) {
		if (webSockets.containsValue(session)) {
			return true;
		}
		return false;
	}

	/**
	 * 获得客户端个数
	 * 
	 * @return
	 */
	public Integer getClientCount() {
		return webSockets.size();
	}

	/**
	 * 移除指定客户端
	 * 
	 * @param wss
	 */
	public void removeSession(WebSocketSession session) {
		HttpSession httpSession = (HttpSession) session.getAttributes().get(
				"httpsession");
		Integer id = getUserId(httpSession);
		ConcurrentWebSocketSessionDecorator delete = null;
		for (ConcurrentWebSocketSessionDecorator socket : webSockets.get(id)) {
			if (session.getId().equals(socket.getId())) {
				delete = socket;
			}
		}
		removeById(id, delete);
	}

	/**
	 * 添加新的客户端
	 * 
	 * @param session
	 */
	public boolean addSession(WebSocketSession session) {
		HttpSession httpSession = (HttpSession) session.getAttributes().get(
				"httpsession");
		final int userId = getUserId(httpSession);

		if (!isExistSessionId(userId, session)) {
			return webSockets.put(userId, packagingSession(session));
		}
		return false;
	}

	/**
	 * 通过用户Id移除指定的客户端
	 * 
	 * @param userId
	 * @param session
	 */
	public void removeById(int userId,
			ConcurrentWebSocketSessionDecorator session) {
		closeSocket(session);
		webSockets.remove(userId, session);
	}

	/**
	 * 关闭指定的websocket
	 * 
	 * @param socket
	 */
	public void closeSocket(ConcurrentWebSocketSessionDecorator socket) {
		if (socket.isOpen()) {
			try {
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public ConcurrentWebSocketSessionDecorator packagingSession(
			WebSocketSession session) {
		return new ConcurrentWebSocketSessionDecorator(session, 10000, 1024);
	}

	/**
	 * 包装数据
	 * 
	 * @param type
	 * @param data
	 * @param msg
	 * @return
	 */
	public static String packagingData(int type, String msg, Object data) {
		WSResponseDto wsDto = new WSResponseDto(type);
		wsDto.setData(data);
		wsDto.setDesc(msg);
		return JSON.toJSONString(wsDto);
	}

	public static int getUserId(HttpSession session) {
		if (session == null) {
			return -1;
		}
		if (!(session.getAttribute(Constants.LOGIN_KEY) instanceof User)) {
			return -1;
		}
		User user = (User) session.getAttribute(Constants.LOGIN_KEY);
		return user.getId();
	}

	private boolean isExistSessionId(Integer userId, WebSocketSession session) {
		for (Iterator<ConcurrentWebSocketSessionDecorator> iter = webSockets
				.get(userId).iterator(); iter.hasNext();) {
			if (iter.next().getId().equals(session.getId())) {
				return true;
			}
		}
		return false;
	}

	protected void destroy() {
		try {
			this.webSockets.values().forEach(e -> {
				closeSocket(e);
			});
			this.webSockets.keys().forEach(e -> {
				this.webSockets.removeAll(e);
			});
		} catch (Exception e) {
			LOGGER.error("shutdown error. ", e);
		}
	}
}
